package com.bruce.pullrefresh;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;

/**
 * 描述当前类的作用
 *
 * @author LiXiaoLong
 * @version 1.0
 * @since 2016-04-19 17:06
 */
public class PullToRefreshProgressBar extends View {
    private static final float MIN_SWEEP = 15f;
    private static final int DEFAULT_COLOR = 0xfff54545;

    private Paint paint;
    private int size = 0;
    private RectF bounds;

    private boolean autoStart;
    private float sweep, rotateOffset;
    private int thickness, color, animDuration, animSteps;

    // Animation related stuff
    private float startAngle;
    private ValueAnimator animator;
    private AnimatorSet animSet;

    public PullToRefreshProgressBar(Context context) {
        super(context);
        init(null, 0);

    }

    public PullToRefreshProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public PullToRefreshProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }


    protected void init(AttributeSet attrs, int defStyle) {
        initAttributes(attrs, defStyle);
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        updatePaint();
        bounds = new RectF();
    }

    private void initAttributes(AttributeSet attrs, int defStyle) {
        final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.PullToRefreshProgressBar, defStyle, 0);
        int accentColor = getResources().getIdentifier("colorAccent", "attr", getContext().getPackageName());

        // Initialize attributes from styleable attributes
        thickness = a.getDimensionPixelSize(R.styleable.PullToRefreshProgressBar_ptr_circleThickness, 8);
        autoStart = a.getBoolean(R.styleable.PullToRefreshProgressBar_ptr_autoStart, true);
        startAngle = a.getFloat(R.styleable.PullToRefreshProgressBar_ptr_startAngle, -90);

        // If color explicitly provided
        if (a.hasValue(R.styleable.PullToRefreshProgressBar_ptr_circleColor)) {
            color = a.getColor(R.styleable.PullToRefreshProgressBar_ptr_circleColor, DEFAULT_COLOR);
        }else if (accentColor != 0) {// If using support library v7 accentColor
            TypedValue t = new TypedValue();
            getContext().getTheme().resolveAttribute(accentColor, t, true);
            color = t.data;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {// If using native accentColor (SDK >21)
            TypedArray t = getContext().obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
            color = t.getColor(0, DEFAULT_COLOR);
            t.recycle();
        } else {
            //Use default color
            color = DEFAULT_COLOR;
        }
        animDuration = a.getInteger(R.styleable.PullToRefreshProgressBar_ptr_animDuration, 3500);
        animSteps = a.getInteger(R.styleable.PullToRefreshProgressBar_ptr_animStep, 3);
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int xPad = getPaddingLeft() + getPaddingRight();
        int yPad = getPaddingTop() + getPaddingBottom();
        int width = getMeasuredWidth() - xPad;
        int height = getMeasuredHeight() - yPad;
        size = (width < height) ? width : height;
        setMeasuredDimension(size + xPad, size + yPad);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        size = (w < h) ? w : h;
        updateBounds();
    }

    private void updateBounds() {
        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();
        bounds.set(paddingLeft + thickness, paddingTop + thickness, size - paddingLeft - thickness, size - paddingTop - thickness);
    }

    private void updatePaint() {
        paint.setColor(color);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(thickness);
        paint.setStrokeCap(Paint.Cap.BUTT);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawArc(bounds, startAngle + rotateOffset, sweep, false, paint);
    }

    /**
     * Starts the progress bar animation.
     * (This is an alias of resetAnimation() so it does the same thing.)
     */
    public void startAnimation() {
        resetAnimation();
    }

    /**
     * Resets the animation.
     */
    public void resetAnimation() {
        // Cancel all the old animators
        if (animator != null && animator.isRunning()) {
            animator.cancel();
        }
        if (animSet != null && animSet.isRunning()) {
            animSet.cancel();
        }
        sweep = MIN_SWEEP;
        animSet = new AnimatorSet();
        AnimatorSet prevSet = null, nextSet;
        for (int k = 0; k < animSteps; k++) {
            nextSet = createAnimator(k);
            AnimatorSet.Builder builder = animSet.play(nextSet);
            if (prevSet != null)
                builder.after(prevSet);
            prevSet = nextSet;
        }

        // Listen to end of animation so we can infinitely loop
        animSet.addListener(new AnimatorListenerAdapter() {
            boolean wasCancelled = false;

            @Override
            public void onAnimationCancel(Animator animation) {
                wasCancelled = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                if (!wasCancelled)
                    resetAnimation();
            }
        });
        animSet.start();
    }

    /**
     * Stops the animation
     */

    public void stopAnimation() {
        if (animator != null) {
            animator.cancel();
            animator = null;
        }
        if (animSet != null) {
            animSet.cancel();
            animSet = null;
        }
    }

    // Creates the animators for one step of the animation
    private AnimatorSet createAnimator(float step) {
        final float maxSweep = 360f * (animSteps - 1) / animSteps + MIN_SWEEP;
        final float start = -90f + step * (maxSweep - MIN_SWEEP);

        // Extending the front of the arc
        ValueAnimator frontEndExtend = ValueAnimator.ofFloat(MIN_SWEEP, maxSweep);
        frontEndExtend.setDuration(animDuration / animSteps / 2);
        frontEndExtend.setInterpolator(new DecelerateInterpolator(1));
        frontEndExtend.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                sweep = (Float) animation.getAnimatedValue();
                invalidate();
            }
        });

        // Overall rotation
        ValueAnimator rotateAnim1 = ValueAnimator.ofFloat(step * 720f / animSteps, (step + .5f) * 720f / animSteps);
        rotateAnim1.setDuration(animDuration / animSteps / 2);
        rotateAnim1.setInterpolator(new LinearInterpolator());
        rotateAnim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rotateOffset = (Float) animation.getAnimatedValue();
            }
        });

        // Followed by...

        // Retracting the back end of the arc
        ValueAnimator backEndRetract = ValueAnimator.ofFloat(start, start + maxSweep - MIN_SWEEP);
        backEndRetract.setDuration(animDuration / animSteps / 2);
        backEndRetract.setInterpolator(new DecelerateInterpolator(1));
        backEndRetract.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                startAngle = (Float) animation.getAnimatedValue();
                sweep = maxSweep - startAngle + start;
                invalidate();
            }
        });

        // More overall rotation
        ValueAnimator rotateAnim2 = ValueAnimator.ofFloat((step + .5f) * 720f / animSteps, (step + 1) * 720f / animSteps);
        rotateAnim2.setDuration(animDuration / animSteps / 2);
        rotateAnim2.setInterpolator(new LinearInterpolator());
        rotateAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rotateOffset = (Float) animation.getAnimatedValue();
            }
        });

        AnimatorSet set = new AnimatorSet();
        set.play(frontEndExtend).with(rotateAnim1);
        set.play(backEndRetract).with(rotateAnim2).after(rotateAnim1);
        return set;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (autoStart) {
            startAnimation();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimation();
    }

    @Override
    public void setVisibility(int visibility) {
        int currentVisibility = getVisibility();
        super.setVisibility(visibility);
        if (visibility != currentVisibility) {
            if (visibility == View.VISIBLE) {
                resetAnimation();
            } else if (visibility == View.GONE || visibility == View.INVISIBLE) {
                stopAnimation();
            }
        }
    }
}

